﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Threading;
using Microsoft.Samples.ServiceHosting.StorageClient;

namespace BlobExplorer
{

    public class TransferEventArgs : EventArgs
    {
        public TransferEventArgs(List<string> uploadNames, List<string> downloadNames)
        {
            UploadNames = uploadNames;
            DownloadNames = downloadNames;
        }
        public List<string> UploadNames { get; private set; }
        public List<string> DownloadNames { get; private set; }
    }

    public class BlobModel
    {
        public ObservableCollection<BlobContainer> Containers { get; private set; }
        public ObservableCollection<BlobProperties> Blobs { get; private set; }

        BlobStorage storage;

        public event EventHandler<TransferEventArgs> TransferListChanged = delegate { };

        private SynchronizationContext viewSyncCtx;

        internal BlobModel(SynchronizationContext syncCtx)
        {
            viewSyncCtx = syncCtx;
            Containers = new ObservableCollection<BlobContainer>();
            Blobs = new ObservableCollection<BlobProperties>();
        }

        #region Upload/download notification

        private List<string> uploadNames = new List<string>();
        private List<string> downloadNames = new List<string>();

        internal void AddUpload(string name)
        {
            lock (uploadNames)
            {
                uploadNames.Add(name);
            }

            GenerateTransferEvent();
        }

        internal void RemoveUpload(string name)
        {
            lock (uploadNames)
            {
                uploadNames.Remove(name);
            }

            GenerateTransferEvent();
        }

        internal void AddDownload(string name)
        {
            lock (downloadNames)
            {
                downloadNames.Add(name);
            }

            GenerateTransferEvent();
        }

        internal void RemoveDownload(string name)
        {
            lock (downloadNames)
            {
                downloadNames.Remove(name);
            }

            GenerateTransferEvent();
        }

        private void GenerateTransferEvent()
        {
            List<string> uploadList = null;
            List<string> downloadList = null;
            lock (uploadNames)
            {
                lock (downloadNames)
                {
                    uploadList = uploadNames.ToList();
                    downloadList = downloadNames.ToList();
                }
            }

            TransferEventArgs e = new TransferEventArgs(uploadList, downloadList);

            viewSyncCtx.Post(delegate { TransferListChanged(this, e); }, null);

        }

        #endregion

        internal void AddBlob(BlobContainer container, string selectedBlobName, string fileName, NameValueCollection metadata)
        {
            string blobName = selectedBlobName;
            FileInfo fi = new FileInfo(fileName);
            BlobProperties props = new BlobProperties(blobName) { ContentType = Utilities.GetContentType(System.IO.Path.GetExtension(fileName)), Metadata = metadata };
            BlobContents blob = new BlobContents(fi.OpenRead());

            BlobProperties prop = null;
            Action a = delegate
            {
                container.CreateBlob(props, blob, true);

                prop = container.GetBlobProperties(blobName);
            };

            AsyncCallback cb = delegate(IAsyncResult ar)
            {
                a.EndInvoke(ar);

                SendOrPostCallback doOnUIThread = delegate
                {
                    var existingList = from b in Blobs
                                       where prop.Name == b.Name
                                       select b;
                    if (existingList.Count() == 1)
                    {
                        var existing = existingList.First();
                        Blobs.Remove(existing);
                    }

                    Blobs.Add(prop);
                };
                viewSyncCtx.Post(doOnUIThread, null);
                RemoveUpload(blobName);
            };

            AddUpload(blobName);
            a.BeginInvoke(cb, null);
        }

        internal void PopulateBlobs(BlobContainer container)
        {
            Blobs.Clear();

            IEnumerable<object> items = null;

            Action a = delegate
            {
                items = container.ListBlobs("", false);
            };

            AsyncCallback cb = delegate(IAsyncResult ar)
            {
                a.EndInvoke(ar);

                viewSyncCtx.Post(delegate
                {
                    foreach (object item in items)
                    {
                        BlobProperties prop = item as BlobProperties;
                        if (prop != null)
                        {
                            Blobs.Add(prop);
                        }
                    }
                }, null);
            };

            a.BeginInvoke(cb, null);
        }

        internal void DeleteContainer(BlobContainer container)
        {
            Containers.Remove(container);
            container.DeleteContainer();
        }

        internal void AddNewContainer(string name, bool publicContainer)
        {
            BlobContainer container = storage.GetBlobContainer(name.ToLower());

            container.CreateContainer(new NameValueCollection(),
                                      publicContainer ? ContainerAccessControl.Public : ContainerAccessControl.Private);

            Containers.Add(container);
        }

        internal void Disconnect()
        {
            Blobs.Clear();
            Containers.Clear();

            storage = null;
        }

        internal void Connect(StorageAccountInfo info)
        {
            storage = BlobStorage.Create(info);
            storage.Timeout = TimeSpan.FromMinutes(5);

            PopulateContainers();
        }

        internal void PopulateContainers()
        {
            Containers.Clear();
            Blobs.Clear();

            foreach (var container in storage.ListBlobContainers())
            {
                Containers.Add(container);
            }
        }

        internal void GetBlob(BlobContainer container, BlobProperties prop, string filename)
        {
            FileStream fs = File.OpenWrite(filename);

            BlobContents blob = new BlobContents(fs);

            AddDownload(prop.Name);

            Action a = delegate
            {
                container.GetBlob(prop.Name, blob, false);

                RemoveDownload(prop.Name);
                fs.Close();
            };

            a.BeginInvoke(ar => a.EndInvoke(ar), null);

        }

        internal void DeleteBlobs(IEnumerable<BlobProperties> blobsToDelete, BlobContainer container)
        {
            foreach (BlobProperties item in blobsToDelete)
            {
                container.DeleteBlob(item.Name);
            }

            PopulateBlobs(container);
        }

        internal BlobProperties GetBlobProperties(BlobContainer container, string name)
        {
            return container.GetBlobProperties(name);
        }
    }
}
